#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <regex>
#include "cdb_to_inp_convertor.h"

CdbToInpConvertor::CdbToInpConvertor() {
}

CdbToInpConvertor::~CdbToInpConvertor() {
}

int CdbToInpConvertor::convert(const std::string &cdbPath, const std::string &inpPath) {
    int parseResult = parseCdbFile(cdbPath);
    if (parseResult != 0) {
        return parseResult;
    }

    return generateInpFile(inpPath);
}

int CdbToInpConvertor::parseCdbFile(const std::string &path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        std::cerr << "Error opening CDB file: " << path << std::endl;
        return -1;
    }

    std::string line;
    std::string currentSection = "OTHER";

    while (getline(file, line)) {
        trim(line);
        if (line.empty()) {
            continue;
        }
        if (line.size() >= 4 && line.substr(0, 4) == "C***") {
            continue;
        }

        if (line.size() >= 2 && line.substr(0, 2) == "N,") {
            currentSection = "OTHER";
        }
        else if (line == "-1") {
            currentSection = "OTHER";
        }
        else if (line.find("NBLOCK") != std::string::npos) {
            currentSection = "NODE";
        }
        else if (line.find("EBLOCK")  != std::string::npos) {
            currentSection = "ELEMENT";
        }
        else if (line.find("SECTYPE") != std::string::npos) {
            currentSection = "SECTYPE";
        }

        if (currentSection == "NODE") {
            parseNode(line);
        } else if (currentSection == "ELEMENT") {
            parseElement(line);
        } else if (currentSection ==  "SECTYPE") {
            parsePropert(line);
        }
    }

    return 0;
}

void CdbToInpConvertor::parseNode(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (iss >> token) {
        if (token == "-1") break; // 处理异常终止符
        tokens.push_back(token);
    }

    if (tokens.size() < 6) {
        return;
    }

    Node tmpNode;
    tmpNode.id = std::stoi(tokens[0]);
    tmpNode.x = std::stod(tokens[3]);
    tmpNode.y = std::stod(tokens[4]);
    tmpNode.z = std::stod(tokens[5]);
    gridNodes.push_back(tmpNode);
}

void CdbToInpConvertor::parseElement(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (iss >> token) {
        if (token == "-1") break; // 处理异常终止符
        tokens.push_back(token);
    }

    if (tokens.size() < 2) {
        return ;
    }

    Element elem;
    elem.type = std::stoi(tokens[0]);

    // CQUAD4
    if (elem.type == 705002) {
        int nodeCount = std::stoi(tokens[8]);
        elem.id = std::stoi(tokens[10]);
        elem.propertyID =  std::stoi(tokens[2]);
        for (int i = 11; i < 11 + nodeCount; i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }
    }
    // CHEXA
    else if (elem.type == 704002) {
        elem.id = std::stoi(tokens[1]);
        for (std::size_t i = 3; i < tokens.size(); i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }
    }
    else if (elem.type == 702002) {
        elem.id = std::stoi(tokens[1]);
         for (int i = 3; i < 3 + 4; i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }
    }
    else {
        std::cout << "Unsupported element: " << line << std::endl;
        return;
    }
    gridElements.push_back(elem);
}

void CdbToInpConvertor::parsePropert(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (std::getline(iss, token, ',')) {
        tokens.push_back(token);
    }

    if (tokens.size() < 2) {
        return ;
    }

    if (tokens[0] == "SECTYPE") {
        Property tmpProperty;
        tmpProperty.id = std::stoi(tokens[1]);
        tmpProperty.type = tokens[2];
        properties.push_back(tmpProperty);
    } else if (tokens[0] == "SECDATA") {
        properties.back().thickness = std::stod(tokens[1]);
        properties.back().materialId = std::stoi(tokens[2]);
    }
}

int CdbToInpConvertor::generateInpFile(const std::string &path) {
    std::ofstream outFile(path, std::ios::out);
    if (!outFile) {
        std::cerr << "Error creating INF file: " << path << std::endl;
        return -1;
    }
    
    outFile << "*Node" << std::endl;
    for (const auto& node : gridNodes) {
        outFile << node.id << "," << node.x << "," << node.y << "," << node.z << std::endl;
    }

    outFile << "*Element, type=S4R" << std::endl;
    for (const auto& elem : gridElements) {
        outFile << elem.id;
        for (int nid : elem.nodes) {
            outFile << "," << nid;
        }
        outFile << std::endl;
    }

    for (const auto &property: properties) {
        int beginElemId = 0;
        int endElemId = 0;
        for (const auto& elem : gridElements) {
            if (elem.propertyID == property.id) {
                if (beginElemId == 0) {
                    beginElemId = elem.id;
                }
                endElemId = elem.id;
            }
	}

        outFile << "*Elset, elset=PSHELL_" << property.id << ", generate" << std::endl;
	outFile << beginElemId << "," << endElemId << "," << 1 << std::endl;
    }

    for (const auto &property: properties) {
        outFile << "*Shell Section, elset=PSHELL_" << property.id << ", material=MAT_MID_" << property.materialId << ", controls=EC-1, offset=PART-1_PSHELL_" << property.id << "_OFFSET_DIST" << std::endl;
        outFile << property.thickness << ", " << 5 << std::endl;
    }
    return 0;
}

void CdbToInpConvertor::trim(std::string& s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

